#region Imported Namespaces

using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

#endregion

namespace Christian.Helle.PocketNews.PocketPC.Controls
{
    public class OwnerDrawnList<T> : Control
    {
        protected SolidBrush backBrush;
        protected Bitmap cachedBackground;
        protected bool isUpdating;
        protected int itemHeight = -1;
        private Point mousePoint = new Point(0, 0);
        protected StringFormat nwFormat;

        protected Bitmap offScreen;
        protected Pen pen;
        private int scrollValue;
        protected int scrollWidth;
        protected SolidBrush selectedBrush;
        protected SolidBrush selectedTextBrush;
        private int selectedIndex = -1;
        private bool startGesture;
        protected SolidBrush textBrush;
        protected int topleft = 3;
        protected int visibleCount;
        protected VScrollBar vScrollBar;

        public OwnerDrawnList()
        {
            vScrollBar = new VScrollBar();
            scrollWidth = vScrollBar.Width = Dpi.Scale(vScrollBar.Width);

            vScrollBar.Parent = this;
            vScrollBar.Visible = false;
            vScrollBar.SmallChange = 1;
            vScrollBar.ValueChanged += ScrollValueChanged;

            Items = new List<T>();

            visibleCount = GetVisibleCount();

            pen = new Pen(ForeColor);
            textBrush = new SolidBrush(ForeColor);
            backBrush = new SolidBrush(BackColor);
            selectedTextBrush = new SolidBrush(SystemColors.HighlightText);
            selectedBrush = new SolidBrush(SystemColors.Highlight);
            nwFormat = new StringFormat(StringFormatFlags.NoWrap);
        }

        public List<T> Items { get; private set; }

        public int SelectedIndex
        {
            get { return selectedIndex; }
            set
            {
                selectedIndex = value;
                if (SelectedIndexChanged != null)
                {
                    SelectedIndexChanged(this, EventArgs.Empty);
                }
                Invalidate();
            }
        }

        public object SelectedItem
        {
            get
            {
                if (selectedIndex >= 0 && selectedIndex < Items.Count)
                {
                    return Items[selectedIndex];
                }
                return null;
            }
        }

        protected virtual int ItemHeight
        {
            get { return itemHeight; }
            set { itemHeight = value; }
        }

        protected int DrawCount
        {
            get
            {
                if (vScrollBar.Value + vScrollBar.LargeChange > vScrollBar.Maximum)
                {
                    return vScrollBar.Maximum - vScrollBar.Value + 1;
                }
                return vScrollBar.LargeChange;
            }
        }

        protected Bitmap CachedBackground
        {
            get
            {
                if (cachedBackground == null)
                    CreateGradientBackground();
                return cachedBackground;
            }
        }

        public event EventHandler SelectedIndexChanged;

        protected virtual void OnSelectedIndexChanged(EventArgs e)
        {
            if (SelectedIndexChanged != null)
            {
                SelectedIndexChanged(this, e);
            }
        }

        protected void ScrollValueChanged(object o, EventArgs e)
        {
            Invalidate();
        }

        public void EnsureVisible(int index)
        {
            if (index < vScrollBar.Value)
            {
                vScrollBar.Value = index;
            }
            else if (index >= vScrollBar.Value + DrawCount)
            {
                vScrollBar.Value = index - DrawCount + 1;
            }

            //Invalidate();
        }

        public void EnsureVisible()
        {
            EnsureVisible(selectedIndex);
        }

        public void MoveUp()
        {
            if (selectedIndex > vScrollBar.Minimum)
            {
                EnsureVisible(--SelectedIndex);
            }
        }

        public void MoveDown()
        {
            if (selectedIndex < vScrollBar.Maximum)
            {
                EnsureVisible(++SelectedIndex);
            }
        }

        private void PageDown()
        {
            if (selectedIndex < vScrollBar.Maximum)
            {
                if (selectedIndex + visibleCount < Items.Count - 1)
                {
                    SelectedIndex = selectedIndex + visibleCount;
                    EnsureVisible(selectedIndex);
                }
            }
        }

        private void PageUp()
        {
            if (selectedIndex > vScrollBar.Minimum)
            {
                if (selectedIndex - visibleCount >= 0)
                {
                    SelectedIndex = selectedIndex - visibleCount;
                    EnsureVisible(selectedIndex);
                }
            }
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Home:
                    SelectedIndex = 0;
                    EnsureVisible();
                    break;
                case Keys.End:
                    SelectedIndex = Items.Count - 1;
                    EnsureVisible();
                    break;
                case Keys.Down:
                    MoveDown();
                    break;
                case Keys.Up:
                    MoveUp();
                    break;
                case Keys.PageDown:
                    PageDown();
                    break;
                case Keys.PageUp:
                    PageUp();
                    break;
            }

            base.OnKeyDown(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            mousePoint.X = e.X;
            mousePoint.Y = e.Y;
            startGesture = false;
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (startGesture)
                return;

            SelectedIndex = vScrollBar.Value + (e.Y/itemHeight);
            if (SelectedIndex > Items.Count - 1)
            {
                SelectedIndex = -1;
            }

            if (!Focused)
            {
                Focus();
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (!startGesture)
                if (mousePoint.Y - e.Y < itemHeight && e.Y - mousePoint.Y < itemHeight)
                    return;

            if (mousePoint.Y > e.Y)
            {
                scrollValue = vScrollBar.Value + (e.Y/itemHeight);
                if (scrollValue < vScrollBar.Maximum)
                    vScrollBar.Value = scrollValue;
            }
            else if (mousePoint.Y < e.Y)
            {
                scrollValue = vScrollBar.Value - (e.Y/itemHeight);
                if (scrollValue < vScrollBar.Maximum)
                    vScrollBar.Value = scrollValue;
            }
            mousePoint.Y = e.Y;

            startGesture = true;
        }

        protected int GetVisibleCount()
        {
            //return (SystemState.DisplayRotation == 90 || SystemState.DisplayRotation == -90) ? 4 : 7;
            return (Screen.PrimaryScreen.Bounds.Height > Screen.PrimaryScreen.Bounds.Width) ? 7 : 4;
        }

        protected override void OnResize(EventArgs e)
        {
            visibleCount = GetVisibleCount();
            ItemHeight = Height/visibleCount;

            var viewableItemCount = ClientSize.Height/ItemHeight;

            vScrollBar.Bounds = new Rectangle(
                ClientSize.Width - scrollWidth,
                0,
                scrollWidth,
                ClientSize.Height);

            if (offScreen != null)
            {
                offScreen.Dispose();
                offScreen = null;
            }

            if (Items.Count > viewableItemCount)
            {
                vScrollBar.Visible = true;
                vScrollBar.LargeChange = viewableItemCount;
                offScreen = new Bitmap(
                    ClientSize.Width - scrollWidth - 1, ClientSize.Height - 2);
            }
            else
            {
                vScrollBar.Visible = false;
                vScrollBar.LargeChange = Items.Count;
                offScreen = new Bitmap(
                    ClientSize.Width - 1, ClientSize.Height - 2);
            }

            vScrollBar.Maximum = Items.Count - 1;

            CreateGradientBackground();
        }

        private void CreateGradientBackground()
        {
            if (cachedBackground != null)
            {
                cachedBackground.Dispose();
                cachedBackground = null;
            }
            cachedBackground = new Bitmap(offScreen.Width, offScreen.Height);

            using (var gxOff = Graphics.FromImage(cachedBackground))
            {
                var rect = new Rectangle(
                    0,
                    0,
                    ClientSize.Width - 2,
                    ClientSize.Height - 2);

                Gradient.Fill(
                    gxOff,
                    rect,
                    SystemColors.Control,
                    SystemColors.HighlightText,
                    Gradient.FillDirection.RightToLeft);
            }
        }

        public void BeginUpdate()
        {
            isUpdating = true;
        }

        public void EndUpdate()
        {
            isUpdating = false;

            var viewableItemCount = ClientSize.Height/ItemHeight;
            if (Items.Count > viewableItemCount)
            {
                vScrollBar.Visible = true;
                vScrollBar.LargeChange = viewableItemCount;
            }
            else
            {
                vScrollBar.Visible = false;
                vScrollBar.LargeChange = Items.Count;
            }

            vScrollBar.Maximum = Items.Count - 1;

            Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (isUpdating)
                return;

            using (var gxOff = Graphics.FromImage(offScreen))
            {
                gxOff.DrawImage(CachedBackground, 0, 0);
            }
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            e.Graphics.DrawRectangle(
                pen,
                0,
                0,
                ClientSize.Width - 1,
                ClientSize.Height - 1);
        }
    }
}